/*
*	Author: Jacob Dufault
*	Credits: Mat Buckland (fup@ai-junkie.com) for his Vector2D class
*	
*	Date Created: July 8th, 2009
*
*	Desc: A 3 dimensional Vector3D structure.
*/

#ifndef _VECTOR3D_HPP_
#define _VECTOR3D_HPP_

#include "Numerics.hpp"
#include "Defines.hpp"

namespace klasPhysics
{

struct Vector3D
{
	// Data
	Real x, y, z;
	
	// ctors
	Vector3D():x(0.0),y(0.0),z(0.0){}
	Vector3D(Real X, Real Y, Real Z):x(X), y(Y), z(Z){}
	
	// Zeroing functions
	inline void Zero();
	inline bool IsZero() const;

	// Normalize the Vector3D
	inline void Normalize();

	// Functions that return data based upon this Vector3D
	enum { AXIS_X, AXIS_Y, AXIS_Z };
	inline			Vector3D Perp(iReal axis);
	
	inline Real		Length() const;	
	inline Real		LengthSq() const;
	inline Real		Dot(const Vector3D &other) const;
	inline Real		Distance(const Vector3D &other) const;
	inline Vector3D GetReverse() const;
	inline Vector3D Cross(const Vector3D &other) const;
	inline void		Truncate(Real max);
	inline void		Reflect(const Vector3D &normal);
	

	// Operator overloads

	bool operator ==(const Vector3D &rhs);
	bool operator !=(const Vector3D &rhs);

	friend inline Vector3D operator-(const Vector3D &a);
    friend inline Vector3D operator+(const Vector3D &a, const Vector3D &b);
    friend inline Vector3D operator-(const Vector3D &a, const Vector3D &b);
    friend inline Vector3D operator*(const Vector3D &a, const Vector3D &b);
	friend inline Vector3D operator/(const Vector3D &a, const Vector3D &b);
    friend inline Vector3D& operator+=(Vector3D &a, const Vector3D &b);
    friend inline Vector3D& operator-=(Vector3D &a, const Vector3D &b);
    friend inline Vector3D& operator*=(Vector3D &a, const Vector3D &b);

    friend inline Vector3D operator*(const Vector3D &a, Real s);
    friend inline Vector3D operator/(const Vector3D &a, Real s);
    friend inline Vector3D& operator*=(Vector3D &a, Real s);
    friend inline Vector3D& operator/=(Vector3D &a, Real s);
    friend inline Vector3D operator*(Real s, const Vector3D &a);
    friend inline Vector3D& operator*=(Real s, Vector3D &a);

};

// A few more operator overloads
/*const Vector3D operator +(const Vector3D &lhs, const Vector3D &rhs);
const Vector3D operator -(const Vector3D &lhs, const Vector3D &rhs);
const Vector3D operator *(const Vector3D &lhs, const Vector3D &rhs);
const Vector3D operator /(const Vector3D &lhs, const Vector3D &rhs);

const Vector3D operator +(Real lhs, const Vector3D &rhs);
const Vector3D operator -(Real lhs, const Vector3D &rhs);
const Vector3D operator *(Real lhs, const Vector3D &rhs);
const Vector3D operator /(Real lhs, const Vector3D &rhs);

const Vector3D operator +(const Vector3D &lhs, Real rhs);
const Vector3D operator -(const Vector3D &lhs, Real rhs);
const Vector3D operator *(const Vector3D &lhs, Real rhs);
const Vector3D operator /(const Vector3D &lhs, Real rhs);*/

inline Vector3D operator-(const Vector3D &a)
{
    return Vector3D(-a.x, -a.y, -a.z);
}

inline Vector3D operator+(const Vector3D &a, const Vector3D &b)
{
    return Vector3D(a.x+b.x, a.y+b.y, a.z+b.z);
}

inline Vector3D operator-(const Vector3D &a, const Vector3D &b)
{
    return Vector3D(a.x-b.x, a.y-b.y, a.z-b.z);
}

inline Vector3D operator*(const Vector3D &a, const Vector3D &b)
{
    return Vector3D(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}

inline Vector3D operator/(const Vector3D &a, const Vector3D &b)
{
	return Vector3D(a.x/b.x, a.y/b.y, a.z/b.z);
}
inline Vector3D& operator+=(Vector3D &a, const Vector3D &b)
{
    a.x += b.x;
    a.y += b.y;
    a.z += b.z;
    return a;
}

inline Vector3D& operator-=(Vector3D &a, const Vector3D &b)
{
    a.x -= b.x;
    a.y -= b.y;
    a.z -= b.z;
    return a;
}

inline Vector3D& operator*=(Vector3D &a, const Vector3D &b)
{
    const Real cx = a.y * b.z - a.z * b.y;
    const Real cy = a.z * b.x - a.x * b.z;
    const Real cz = a.x * b.y - a.y * b.x;
    a.x = cx;
    a.y = cy;
    a.z = cz;
    return a;
}

inline Vector3D operator*(const Vector3D &a, Real s)
{
    return Vector3D(a.x*s, a.y*s, a.z*s);
}

inline Vector3D operator/(const Vector3D &a, Real s)
{
    kAssert(s!=0);
    return Vector3D(a.x/s, a.y/s, a.z/s);
}

inline Vector3D& operator*=(Vector3D &a, Real s)
{
    a.x *= s;
    a.y *= s;
    a.z *= s;
    return a;
}

inline Vector3D& operator/=(Vector3D &a, Real s)
{
    kAssert(s!=0);
    a.x /= s;
    a.y /= s;
    a.z /= s;
    return a;
}

inline Vector3D operator*(Real s, const Vector3D &a)
{
    return Vector3D(a.x*s, a.y*s, a.z*s);
}

inline Vector3D& operator*=(Real s, Vector3D &a)
{
    a.x *= s;
    a.y *= s;
    a.z *= s;
    return a;
}


}
#endif